package com.nulldev.util.security.encryptAPI;

import java.security.Security;

import com.nulldev.util.manage.MarkedForRemoval;
import com.nulldev.util.security.encryptAPI.impls.a51.A51;
import com.nulldev.util.security.encryptAPI.impls.aes128.AES128;
import com.nulldev.util.security.encryptAPI.impls.aes128_counter.AES128Counter;
import com.nulldev.util.security.encryptAPI.impls.blowfish.Blowfish;
import com.nulldev.util.security.encryptAPI.impls.des.DES;
import com.nulldev.util.security.encryptAPI.impls.ice_thirdparty.Ice;
import com.nulldev.util.security.encryptAPI.impls.ice_thirdparty.IceKey.Levels;
import com.nulldev.util.security.encryptAPI.impls.idea.IDEA;
import com.nulldev.util.security.encryptAPI.impls.nacl_thirdparty.NaClSharedCipher;
import com.nulldev.util.security.encryptAPI.impls.rabbit.Rabbit;
import com.nulldev.util.security.encryptAPI.impls.rc4.RC4Cipher;
import com.nulldev.util.security.encryptAPI.impls.serpent.SerpentCipher;
import com.nulldev.util.security.encryptAPI.impls.xxtea.XXTEA;

@MarkedForRemoval(reason = "Replaced with the rewritten encryptAPI.", plannedRemovalWhen = "3.1")
public class Encryptors {

	/*
	 * TODO: - Add support for Public and Private Key-based encryptors. (solved in
	 * rw-3.1) - Merge encryptorFor into a Map<> - Do something about ICE's level
	 * and try to isolate it there. (solved in rw-3.1)
	 */

	public static enum SystemEncryptors {
		AES128("encryptapi.legacy.aes128", "AES-128", 2),
		AES128_COUNTER("encryptapi.legacy.aes128_counter", "AES-128", 3),
		DES("encryptapi.legacy.des", "DES", 5),
		ICE("encryptapi.tp.ice", "ICE", 6),
		XXTEA("encryptapi.legacy.xxtea", "XXTEA", 7),
		RC4("encryptapi.tp.rc4", "RC4", 8),
		SERPENT("encryptapi.tp.serpent", "Serpent", 9),
		IDEA("encryptapi.tp.idea", "Idea", 10),
		RABBIT("encryptapi.tp.rabbit", "Rabbit", 11),

		/* Ciphers below are marked as experimental */

		NaCl_Shared("encryptapi.tp.nacl_shared", "NaCl-Shared", 12, true),
		A51("encryptapi.tp.a51", "A5/1", 13, true),
		Blowfish("encryptapi.java.blowfish", "Blowfish", 14, true),

		;

		private final String id, fID;
		private final int iID;
		private final boolean isExperimental;

		private SystemEncryptors(final String id, final String fID, final int iID) {
			this(id, fID, iID, false);
		}

		private SystemEncryptors(final String id, final String fID, final int iID, final boolean experimental) {
			this.id = id;
			this.fID = fID;
			this.iID = iID;
			this.isExperimental = experimental;
		}

		public String id() {
			return this.id;
		}

		public int cipherID() {
			return this.iID;
		}

		public boolean isExperimental() {
			return this.isExperimental;
		}

		public static SystemEncryptors get(final String cipher) {
			for (final SystemEncryptors v : values()) {
				if (v.fID.equalsIgnoreCase(cipher)) {
					return v;
				}
			}
			return null;
		}

		public static SystemEncryptors get(final int cipherID) {
			for (final SystemEncryptors v : values()) {
				if (v.cipherID() == cipherID) {
					return v;
				}
			}
			return null;
		}

		public String friendlyID() {
			return this.fID;
		}
	}

	public static Encryptor encryptorFor(final SystemEncryptors encryptor, final byte[] key) {
		return encryptorFor(encryptor, key, 2);
	}

	public static Encryptor encryptorFor(final SystemEncryptors encryptor, final byte[] key, final int level) {
		if (encryptor == SystemEncryptors.AES128) {
			return new AES128(key);
		} else if (encryptor == SystemEncryptors.AES128_COUNTER) {
			return new AES128Counter(key);
		} else if (encryptor == SystemEncryptors.DES) {
			return new DES(key);
		} else if (encryptor == SystemEncryptors.ICE) {
			return new Ice(key, Levels.get(level));
		} else if (encryptor == SystemEncryptors.XXTEA) {
			return new XXTEA(key);
		} else if (encryptor == SystemEncryptors.RC4) {
			return new RC4Cipher(key);
		} else if (encryptor == SystemEncryptors.SERPENT) {
			return new SerpentCipher(key);
		} else if (encryptor == SystemEncryptors.NaCl_Shared) {
			return new NaClSharedCipher(key);
		} else if (encryptor == SystemEncryptors.IDEA) {
			return new IDEA(key);
		} else if (encryptor == SystemEncryptors.RABBIT) {
			return new Rabbit(key);
		} else if (encryptor == SystemEncryptors.A51) {
			return new A51(key);
		} else if (encryptor == SystemEncryptors.Blowfish) {
			return new Blowfish(key);
		} else {
			throw new UnsupportedOperationException("encryptorFor(SystemEncryptors, byte[], int) -> Unknown encryptor: " + encryptor);
		}
	}

	public static Encryptor encryptorFor(final SystemEncryptors encryptor, final String key) {
		return encryptorFor(encryptor, key, 2);
	}

	public static Encryptor encryptorFor(final SystemEncryptors encryptor, final String key, final int level) {
		if (encryptor == SystemEncryptors.AES128) {
			return new AES128(key);
		} else if (encryptor == SystemEncryptors.AES128_COUNTER) {
			return new AES128Counter(key);
		} else if (encryptor == SystemEncryptors.DES) {
			return new DES(key);
		} else if (encryptor == SystemEncryptors.ICE) {
			return new Ice(key, Levels.get(level));
		} else if (encryptor == SystemEncryptors.XXTEA) {
			return new XXTEA(key);
		} else if (encryptor == SystemEncryptors.RC4) {
			return new RC4Cipher(key);
		} else if (encryptor == SystemEncryptors.SERPENT) {
			return new SerpentCipher(key);
		} else if (encryptor == SystemEncryptors.NaCl_Shared) {
			return new NaClSharedCipher(key);
		} else if (encryptor == SystemEncryptors.IDEA) {
			return new IDEA(key);
		} else if (encryptor == SystemEncryptors.RABBIT) {
			return new Rabbit(key);
		} else if (encryptor == SystemEncryptors.A51) {
			return new A51(key);
		} else if (encryptor == SystemEncryptors.Blowfish) {
			return new Blowfish(key);
		} else {
			throw new UnsupportedOperationException("encryptorFor(SystemEncryptors, String, int) -> Unknown encryptor: " + encryptor);
		}
	}

	private static boolean __init = false;
	static {
		if (!__init) {
			try {
				Security.setProperty("crypto.policy", "unlimited");
			} catch (Exception ex) {
			}
			__init = true;
		}
	}
}
